#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>

#define BALEIRO		0
#define CHEO		1
#define MUTEX		2

#define MAL_PARAM 	0
#define XERAL		1
#define PROPIO		2

#define SEM_KEY		0x100
#define SHM_KEY		0x200

#define TRUE		1
#define FALSE		0

#define MAX_ITEMS	50

struct tColaChar {
	int maxNum, num, ppio, final;
	char items[MAX_ITEMS];
};

typedef struct tColaChar tBuffer;

int obterMemoria(int crear) {
	int flags = 0666;
	if (crear) flags = flags | IPC_CREAT | IPC_EXCL;

	int shmid = shmget(SHM_KEY, sizeof(tBuffer), flags);
	if (shmid == -1) sair(XERAL, "Obtendo memoria compartida");
	return shmid;
}

tBuffer *accederAMemoria() {
	tBuffer *tmp = (tBuffer *) shmat(obterMemoria(FALSE), 0, 0);
	if(tmp == NULL) sair(XERAL,NULL);
	return tmp;
}

void liberaMemoria(tBuffer *shmaddr) {
	if(shmdt(shmaddr) == -1) sair(XERAL, NULL);
}

void crearBuffer(items) {
	int shmid = obterMemoria(TRUE);
	tBuffer *pBuffer = (tBuffer *) shmat(shmid, 0, 0);
	if (pBuffer == NULL)
		sair(XERAL,NULL);
	pBuffer->maxNum = items;
	pBuffer->final = 0;
	pBuffer->ppio = 0;
	pBuffer->num = 0;
}

void eliminarMemoria() {
	int shmid = obterMemoria(FALSE);
	if (shmctl(shmid, IPC_RMID, 0) == -1)
		sair(XERAL, "Eliminando memoria compartida");
}

int engadeACola(char item) {
	tBuffer *p = accederAMemoria();
	if (p->num > p->maxNum) return -1; //Nunca se deberia producir, porque esta controlado polos semaforos
	p->items[p->final] = item;
	p->final = (p->final + 1) % p->maxNum;
	p->num++;
	liberaMemoria(p);
	return 0;
}

char extraeDaCola() {
	tBuffer *p = accederAMemoria();
	if (p->num == 0) return -1; //Nunca se deberia producir, porque esta controlado polos semaforos
	int item = p->items[p->ppio];;
	p->ppio = (p->ppio + 1) % p->maxNum;
	p->num--;
	liberaMemoria(p);
	return item;
}

void mostrarBuffer() {
	tBuffer *p = accederAMemoria();
	char s[300];
	if (p->num == 0) {
		sprintf(s, "Contido do buffer: [ ]\n");
	} else {
		sprintf(s, "Contido do buffer: [ %c", p->items[p->ppio]);
		int i;
		for (i = 1; i < p->num; i++) {
			sprintf(s, "%s, %c", s, p->items[(p->ppio+i) % p->maxNum]);
		}
		sprintf(s, "%s ] ", s);
	}
	write(0, s, strlen(s));
	sprintf(s, "ITEMS: %i\n", p->num);
	write(0, s, strlen(s));
	liberaMemoria(p);
}

